package android.contest.alarmclock;

import java.util.ArrayList;
import java.util.Calendar;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.contest.constant.Constant;
import android.contest.db.AlarmDBMgr;
import android.contest.util.Log;
import android.contest.util.TimeUtil;
import android.database.Cursor;
import android.net.Uri;

public class AlarmMgr extends ContentProvider{
	private static AlarmDBMgr alarmdbmgr;
//	private static Context mContext; //modified by WJingjing
	
	public static boolean init(Context context){
//		if(Log.LOGV)Log.v(Log.LOGDB, "AlarmMgr.init() is called");
//		mContext = context;
		if (alarmdbmgr == null)
			alarmdbmgr = AlarmDBMgr.getMgrInstance(context);
//		if(Log.LOGV)Log.v(Log.LOGDB, "AlarmMgr.init(): alarmdbmgr is "+ alarmdbmgr.toString());
		// for testing java.util.Date, android.test.format.Time, java.sql.Time
		// Finding that java.sql.Time is the most convenient.
//		if(true){
//			Time t = new Time(0, 1, 20);
//			if(Log.LOGV)Log.v(Log.LOGALARM, "t is "+t.toString());
//			if(Log.LOGV)Log.v(Log.LOGALARM, "t is "+t.getTime());
//
//			Time t2 = new Time(0,2,0);
//			
//			Time t3 = new Time(t.getTime() + t2.getTime());
//			if(Log.LOGV)Log.v(Log.LOGALARM, "t3 is "+t3.toString());
//		}
		
		
//		Log.v(Log.LOGDB, "***test*** init ***");
//		alarmdbmgr.dump();
//
//		long id = AlarmMgr.creatTaskItem();
//		Log.v(Log.LOGDB, "the new alarm id = "+id);
//		Log.v(Log.LOGDB, "***test*** after creating***");
//		alarmdbmgr.dump();
//		
//		Calendar c = Calendar.getInstance();
//        c.setTimeInMillis(System.currentTimeMillis());
//        Time time_now = new Time(c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE), c.get(Calendar.SECOND));
//		long t1 = time_now.getTime()+5000; // t1 is 5 seconds after now
//		long t2 = new Time(0,0,5).getTime(); // t2 is 5+5=10 seconds after now
//		long t3 = new Time(0,0,5).getTime(); // t3 is 5+5+5=15 seconds after now
//		AlarmMgr.updateTaskItem(id, t1, t2, t3, 7, context);
//		Log.v(Log.LOGDB, "***test*** after updating***");
//		alarmdbmgr.dump();
//		
//		if(AlarmMgr.setTaskEnabled(id, true, context))
//			Log.v(Log.LOGDB, "setTaskEnabled(true) successfully");
//		Log.v(Log.LOGDB, "***test*** after enabling***");
//		alarmdbmgr.dump();
		
//		if(AlarmMgr.setTaskEnabled(id, false, context))
//			Log.v(Log.LOGDB, "setTaskEnabled(false) successfully");
//		Log.v(Log.LOGDB, "***test*** after disabling***");
//		alarmdbmgr.dump();
//		
		
//		AlarmMgr.deleteTaskItem(id, context);
//		Log.v(Log.LOGDB, "***test*** after deleting***");
//		alarmdbmgr.dump();
//		alarmdbmgr.dump();
//		alarmdbmgr.enableDBItem(1);
//		alarmdbmgr.dump();
//		alarmdbmgr.getLatestAlarm();
//		alarmdbmgr.disableDBItem(1);
//		alarmdbmgr.dump();
//		alarmdbmgr.getLatestAlarm();
		
		return true;
	}
	

	/**
	 * 1. put the new Item into DB, with all times to be <b>zero</b> and status to be <b>off</b> </br>
	 * 
	 * @return the alarm_id if create successfully, -1 if fail
	 */
	public static long creatTaskItem(){
		return alarmdbmgr.createDBItem();
	}
	
	/**
	 * 1. update the task item in the <b>alarmEnabledTable</b>. </br>
	 * 2. if this task item is enabled, update the items in the <b> alarmScheduledTable</b>.</br>
	 * 3. cancel the current alarm that has been reported to the OS, choose the latest one, report it. 
	 * @param id
	 * @param T1 the absolute time within a day. in long type milliseconds
	 * @param T2 the relative time with respect of T1 
	 * @param T3 the relative time with respect of T2 
	 * @param period repeat interval of this task item. e.g. to remind every 3 day, then period = 3
	 * @return true if the item specified by <b>id</b> exists in DB; false otherwise
	 */
	public static boolean updateTaskItem(Context context, long id, long T1, long T2, boolean include2, long T3, boolean  include3, int period){
		boolean successflag = alarmdbmgr.updateDBItem( id,  T1,  T2, include2, T3, include3, period);
		setNextAlert(context);
		return successflag;
	}
	

	/**
	 * 1. delete the task item specified by id in the  <b>alarmEnabledTable</b>. </br>
	 * 2. delete the task item if exists in the <b> alarmScheduledTable</b>.</br>
	 * 3. cancel the current alarm that has been reported to the OS, choose the latest one, report it. 
	 * @param id
	 * @return true if the item specified by <b>id</b> exists in DB; false otherwise
	 */
	public static boolean deleteTaskItem(Context context, long id){
		boolean successflag = alarmdbmgr.deletedDBItem( id);
		setNextAlert(context);
		return successflag;
	}
	
	/**
	 * 1. update the task item in the <b>alarmEnabledTable</b>, set or reset the <b>enabled</b> field. </br> 
	 * 2. add the task item into the  <b> alarmScheduledTable</b> if enabling (adding 3 tuples, scheduling </br>3 events at one time). </br>
	 * Remove the item if disabling. 
	 * 3. cancel the current alarm that has been reported to the OS, choose the latest one, report it. 
	 * @param id
	 * @param enabled
	 * @return
	 */
	public static boolean setTaskEnabled(Context context, long id, boolean enabled){
		boolean successflag;
		if(enabled)
			successflag = alarmdbmgr.enableDBItem(id);
		else
			successflag = alarmdbmgr.disableDBItem(id);
		setNextAlert(context);
		return successflag;
	}
	
	/**
	 * Read the IDs of all task items stored in  <b>alarmEnabledTable</b>
	 * @return
	 */
	public static ArrayList<Long> getIDs(){
//		if(Log.LOGV)Log.v(Log.LOGDB, "AlarmMgr.GetID(): alarmdbmgr = "+alarmdbmgr.toString());
		return alarmdbmgr.getIDs();
	}
	
	public static boolean scheduleNextEpoch(long alarm_id) {
		return alarmdbmgr.scheduleNextEpoch(alarm_id);
	}
	
	/**
	 * The only way to set the system's alarm.
	 * Cancel the current alarm, that has been reported OS, then pick one with latest time and report it
	 */
	public static void setNextAlert(Context context) {
		// canceling the current laundry alarm.
		AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
		Intent intent = new Intent(Constant.ALARM_ALERT_ACTION); 
		PendingIntent pendingIntent = PendingIntent.getBroadcast(
				context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); 
		am.cancel(pendingIntent); 
		Log.d(Log.LOGDB, "AlarmMgr.setNextAlart(): cancelling the current laundry alarm");
		
		Log.v(Log.LOGDB, "AlarmMgr.setNextAlart(): dumping from database");
		alarmdbmgr.dump();
		// pick the latest alarm in alarmScheduledTable
//		AlermTypeTime latestalarm = alarmdbmgr.getLatestAlarm();
		
		Cursor cur_latest_alms =  alarmdbmgr.getLatestAlarm2(System.currentTimeMillis());
		int count_latest_alm  = cur_latest_alms.getCount();
		Log.v(Log.LOGDB, "AlarmMgr.setNextAlert():count_latest_alm="+count_latest_alm);
		if(count_latest_alm == 0)// no alarm to be scheduled
			return;

		cur_latest_alms.moveToFirst();
		long latest_alarm_time = cur_latest_alms.getLong(cur_latest_alms.getColumnIndexOrThrow(AlarmDBMgr.COL_ALERT_TIME));
//		int col_item_id = cur_latest_alms.getColumnIndexOrThrow(AlarmDBMgr.COL_ALM_ID);
//		long item_id = cur_latest_alms.getLong(col_item_id);
		
//		if(latestalarm == null) // no alarm to be scheduled
//		if(latest_alarm_time < System.currentTimeMillis()){ // fault tolerant, if the next alert time is earlier than now, schedule the next.
//			alarmdbmgr.deleteAlertedAlarm(latestalarm.alarm_id, latestalarm.alarm_type);
//			alarmdbmgr.updateAlertedAlarm2(latest_alarm_time, item_id);
//			setNextAlert(context);
//			return;
//		}
			
		do{
			long alarm_time_nextstep = -1;
			
			long alarm_id = cur_latest_alms.getLong(cur_latest_alms.getColumnIndexOrThrow(AlarmDBMgr.COL_ALM_ID));
			int type_id = cur_latest_alms.getInt(cur_latest_alms.getColumnIndexOrThrow(AlarmDBMgr.COL_TYPE_ID));
			
			alarm_time_nextstep = alarmdbmgr.getNextStepScheduleTime(alarm_id, type_id);
			
			// report the latest alarm to the android OS
	        Intent intent2 = new Intent(Constant.ALARM_ALERT_ACTION);
	        intent2.putExtra(Constant.ALARM_ID, alarm_id);
	        intent2.putExtra(Constant.LAUNDRY_TYPE, type_id);
	        intent2.putExtra(Constant.ALARM_TIME, latest_alarm_time);
	        intent2.putExtra(Constant.ALARM_TIME_NEXTSTEP, alarm_time_nextstep);
	        PendingIntent sender = PendingIntent.getBroadcast(
	                context, 0, intent2, PendingIntent.FLAG_CANCEL_CURRENT);
	        am.set(AlarmManager.RTC_WAKEUP, latest_alarm_time, sender);
	        String typestr;
	        switch(type_id){
	        case Constant.WASH_CLOTHES:
	        	typestr = "WASH_CLOTHES";
	        	break;
	        case Constant.DRY_CLOTHES:
	        	typestr = "DRY_CLOTHES";
	        	break;
	        case Constant.COLLECT_CLOTHES:
	        	typestr = "COLLECT_CLOTHES";
	        	break;
	        	default:
	        		throw new java.lang.Error();
	        }
	        if (Log.LOGV) {
	        	Calendar c = Calendar.getInstance();
	        	c.setTimeInMillis(latest_alarm_time);
	        	Log.v(Log.LOGDB, "AlarmMgr.setNextAlart(): set next alarm alarm_id="+ alarm_id +", type="+ typestr+ ", alert_time="+ c.getTime().toString());
	        	c.setTimeInMillis(System.currentTimeMillis());
	        	Log.v(Log.LOGDB, "AlarmMgr.setNextAlart(): time now is " + c.getTime().toString());
	        }
		}while(cur_latest_alms.moveToNext());
		
		cur_latest_alms.close();
	}


	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		// TODO Auto-generated method stub
		return 0;
	}


	@Override
	public String getType(Uri uri) {
		// TODO Auto-generated method stub
		return null;
	}


	@Override
	public Uri insert(Uri uri, ContentValues values) {
		// TODO Auto-generated method stub
		return null;
	}


	@Override
	public boolean onCreate() {
		return AlarmMgr.init(getContext());
	}


	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		// TODO Auto-generated method stub
		return null;
	}


	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
		// TODO Auto-generated method stub
		return 0;
	}

	public static void dump() {
		alarmdbmgr.dump();
	}
	
	public static void dumpEnabledTable(){
		alarmdbmgr.dumpEnabledTable();
	}
	
	public static void dumpScheduledTable(){
		alarmdbmgr.dumpScheduledTable();
	}


	public static boolean scheduleNextEpoch2(long m_itemId) {
		return alarmdbmgr.scheduleNextEpoch2(m_itemId);
		
	}


	public static boolean updateAlertedAlarm2(long latest_alarm_time, long m_itemId) {
		// TODO Auto-generated method stub
		return alarmdbmgr.updateAlertedAlarm2(latest_alarm_time, m_itemId);
	}
}
